package com.syf.core.engine;

import com.syf.core.config.SyApplicationConfiguration;
import com.syf.core.config.SyfPropertySource;
import com.syf.core.cont.SyfCoreCont;
import com.syf.core.test.SyFrameTest;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.env.EnvironmentPostProcessor;
import org.springframework.boot.logging.DeferredLog;
import org.springframework.core.env.CompositePropertySource;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.PropertiesPropertySource;
import org.springframework.core.io.support.SpringFactoriesLoader;

import java.util.List;
import java.util.Properties;

/**
 * EnvironmentPostProcessor 是Spring框架中的一个接口，用于在Spring应用上下文加载之后，但在BeanFactory创建之前，对环境变量进行后处理.
 * <p>
 * 此处我们拓展为SPI机制，当通过SPI机制发现接口：com.syf.core.config.SyfPropertySource存在SPI定义，则加载最早的PropertySource，
 * 通过SPI机制，我们可以插拔式加载配置中心中配置参数，例如apollo、nacos、自定义配置中心等等.
 * <p>
 * EnvironmentPostProcessor，先于ApplicationContextInitializer执行.
 */
public class SyfEnvironmentPostProcessor implements EnvironmentPostProcessor {
    /**
     * 延迟log
     */
    private final DeferredLog log = new DeferredLog();

    /**
     * Step-0
     */
    @Override
    public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
        // 1、获取应用程序的启动类的package作为应用系统的组件扫描基础类路径
        String appBaseScanPackage = application.getMainApplicationClass().getPackage().getName();
        // 1.1 判断是否测试应用
        boolean isTestApp = false;
        Class<?> mainApplicationClass = application.getMainApplicationClass();
        SyFrameTest annotation = mainApplicationClass.getAnnotation(SyFrameTest.class);
        if (annotation != null) {
            appBaseScanPackage = annotation.appBasePackage();
            isTestApp = true;
        }

        log.info("当前系统基础类路径：" + appBaseScanPackage);
        SyApplicationConfiguration.setAppBasePackage(appBaseScanPackage);

        // 设置是否为测试app
        Properties p = new Properties();
        p.setProperty(SyfCoreCont.TEST_RUNNER_FLAG_KEY, Boolean.toString(isTestApp));
        PropertiesPropertySource isTestRunner = new PropertiesPropertySource("isTestRunnerConfig", p);
        environment.getPropertySources().addFirst(isTestRunner);

        // 由于mybatis-plus会导致springboot启用DataSourceAutoConfiguration，而导致程序启动失败。因此，我们手工加入配置，禁用DataSourceAutoConfiguration
        Properties properties = new Properties();
        properties.setProperty("spring.autoconfigure.exclude", "org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration");
        PropertiesPropertySource excludeDataSourceAutoConfig = new PropertiesPropertySource("excludeDataSourceAutoConfig", properties);
        environment.getPropertySources().addFirst(excludeDataSourceAutoConfig);

        // 2、加载SPI扩展syfPropertySources。可以自主设置配置参数源，可以是关系型数据库，可以是其他配置文件
        CompositePropertySource defaultPS = new CompositePropertySource(SyfCoreCont.DEFAULT_PROPERTY_SOURCE_NAME);
        List<SyfPropertySource> syfPropertySources = SpringFactoriesLoader.loadFactories(SyfPropertySource.class, Thread.currentThread().getContextClassLoader());
        if (syfPropertySources != null && !syfPropertySources.isEmpty()) {
            log.warn("通过SPI加载自定义SyfPropertySource的类路径：" + syfPropertySources.get(1).getClass().getTypeName());
            defaultPS.addFirstPropertySource(syfPropertySources.get(1));
        }

        // 2.1 defaultPS放在最前面，取值时，以此处数据为准.
        environment.getPropertySources().addFirst(defaultPS);

    }
}
